package com.utilities.parsers;

import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;

import com.utilities.IO.FileUtilities;
import com.utilities.dataStructures.Range;
import com.utilities.parsers.ContigParser.ParseAllContigsFileParser;
import com.utilities.parsers.ContigParser.ParseEvents;
import com.utilities.parsers.ContigParser.ParseListener;
import com.utilities.pipelineScripts.RangeComparator;

public class ContigsStatsGenerator extends ParseListener
{
	// input parameters
	private String inputScaffoldFile = null;
	private String out_scaffoldStatsFile = null;
	private String out_gapStatsFile = null;
	private String out_summaryFile = null;
	private int referenceSize = 0;
	private boolean considerGaps = true;
	private String strainName = null;
	private boolean appendSummary = false;
	private boolean collectGenes = false;
	private boolean collectCds = false;
	private boolean collectNcRNAs = false;
	private boolean colecttRNAs = false;
	private boolean collecttmRNAs = false;
	private boolean collectrRNAs = false;
	private static ContigsStatsGenerator scaffoldStatsGenerator = null;
	
	// parameters internally used
	private int totalContigLength = 0;
	private int totalMappedLength = 0;
	private int totalCGContent = 0;
	private int cgContent = 0;
	private int totalGapSize = 0;
	private int totalNumberOfReads = 0;
	private boolean referenceGiven = true;
	
	private ArrayList<Range> eachContigInfo = null; 
	private ArrayList<Range> eachGapInfo = null;
	
	private int seqReadLength = 0;
	private Range temp_range_contig = null;
	
	
	private ContigsStatsGenerator()
	{
		eachContigInfo = new ArrayList<Range>();
		eachGapInfo = new ArrayList<Range>();
	}
	
	public static ContigsStatsGenerator getInstance()
	{
		if (scaffoldStatsGenerator == null)
		{
			scaffoldStatsGenerator = new ContigsStatsGenerator();
		}
		else
		{
			scaffoldStatsGenerator.clear();
		}
		return scaffoldStatsGenerator;
	}
	
	
	private void clear() 
	{
		totalContigLength = 0;
		cgContent = 0;
		totalGapSize = 0;
		totalNumberOfReads = 0;
		eachContigInfo = new ArrayList<Range>();
		eachGapInfo = new ArrayList<Range>();
		totalCGContent = 0;
		referenceGiven = true;
		temp_range_contig = null;
		seqReadLength = 0;
		considerGaps = true;
		appendSummary = false;
		out_summaryFile = null;
		strainName = null;
		
	}

	public int getTotalContigLength() {
		return totalContigLength;
	}

	public void setTotalContigLength(int totalContigLength) {
		this.totalContigLength = totalContigLength;
	}

	public int getTotalMappedLength() {
		return totalMappedLength;
	}

	public void setTotalMappedLength(int totalMappedLength) {
		this.totalMappedLength = totalMappedLength;
	}

	public int getTotalCGContent() {
		return totalCGContent;
	}

	public void setTotalCGContent(int totalCGContent) {
		this.totalCGContent = totalCGContent;
	}

	public int getTotalGapSize() {
		return totalGapSize;
	}

	public void setTotalGapSize(int totalGapSize) {
		this.totalGapSize = totalGapSize;
	}

	public int getTotalNumberOfReads() {
		return totalNumberOfReads;
	}

	public void setTotalNumberOfReads(int totalNumberOfReads) {
		this.totalNumberOfReads = totalNumberOfReads;
	}

	public boolean generateStats()
	{
		ParseAllContigsFileParser contigsFileParser = new ParseAllContigsFileParser();
		contigsFileParser.addListener(this);
		try
		{
			contigsFileParser.parse(new FileReader(inputScaffoldFile));
			if (out_summaryFile == null)
			{
				generateSummary(System.out);
			}
			else
			{
				File fl = new File(out_summaryFile);
				if (appendSummary && fl.exists())
					generateSummary(new FileOutputStream(out_summaryFile,appendSummary));
				else
					generateSummary(new FileOutputStream(out_summaryFile));
			}
			
			if (considerGaps && referenceGiven && out_gapStatsFile != null)
			{
				FileUtilities.WriteToFile(eachGapInfo, out_gapStatsFile, 2);
			}
			
			if (out_scaffoldStatsFile != null)
			{
				FileUtilities.WriteToFile(eachContigInfo, out_scaffoldStatsFile, 2);
			}
			
		}
		catch(Exception e)
		{
			e.printStackTrace();
			return false;
		}
		
		
		return true;
	}
	
	private void generateSummary(OutputStream out) 
	{
		PrintWriter pw = new PrintWriter(out);
		if (strainName != null)
		pw.println("Strain Name\t" + strainName);
		pw.println("Size of the reference " + referenceSize);
		pw.println("Total number of CG's " + totalCGContent);
		pw.println("Number of contigs " + eachContigInfo.size());
		pw.println("Total length of contigs " + totalContigLength);
		if (considerGaps && referenceGiven)
		{
			pw.println("Total mapped length " + totalMappedLength);
			pw.println("Number of gaps " + eachGapInfo.size());
		}
		else
		{
			pw.println("No mapping infor provided in the scaffold file");
		}
		if (totalNumberOfReads > 0)
		{
			pw.println("Total number of reads " + totalNumberOfReads);
		}
		pw.println("***************************************");
		pw.flush();
		pw.close();
	}

	@Override
	public void handleEvent(ParseEvents event) {
		try
		{
			switch((ParseEvents.EventTypes)event.getEventType())
			{
			case docStarted:
				 break;
				case contigStarted: 
				{
					String readLine = (String)event.getSource();
					
					// replace all the multiple spaces with a single space and then split
					String[] params = readLine.replaceAll("\\b\\s{2,}\\b", " ").split(" ");
					// >contigName therefore a substring
					String contigName = params[0].trim().substring(1);
					
					if (params.length < 3)
					{
						referenceGiven = false;
					}
					int refStart_curr = 0;
					int refEnd_curr = 0;
					if (referenceGiven)
					{
						refStart_curr = Integer.parseInt(params[2].split("\\.\\.")[0]);
						refEnd_curr = Integer.parseInt(params[2].split("\\.\\.")[1]);
					}
					
					String orientation = "+";
					
					String numberOfReads = "-";
					if (params.length == 5)
					{
						numberOfReads = params[4].trim().split("=")[1]; 
					}
					
					if (referenceGiven && refEnd_curr < refStart_curr)
					{
						int temp = refEnd_curr ; refEnd_curr = refStart_curr; refStart_curr = temp;
						orientation = "-";
					}
					
					if (temp_range_contig != null)
					{
						String str = String.format("%s\t", temp_range_contig.getMetadata(),seqReadLength,cgContent);
						temp_range_contig.setMetadata(str);
						eachContigInfo.add(temp_range_contig);
						
						totalContigLength = totalContigLength + seqReadLength;
						totalCGContent = totalCGContent + cgContent;
						seqReadLength = 0;
						cgContent = 0;
						if (referenceGiven)
						{
							totalMappedLength = totalMappedLength + 
							(temp_range_contig.getUpperLimit()- temp_range_contig.getLowerLimit() + 1);
							if (considerGaps)
							{
								int gap_start = temp_range_contig.getUpperLimit() + 1;
								int gap_end = refStart_curr - 1;
								int length = gap_end - gap_start + 1;
								str = String.format("%s\t%s%s\t%s\t%s",eachGapInfo.size(),gap_start,gap_end,length,temp_range_contig.getMetadata().split("\t")[0]);
								Range gap = new Range(gap_start, gap_end,str);
								eachGapInfo.add(gap);
								totalGapSize = totalGapSize + length;
							}
						}
					}
					
					String str = String.format("%s\t%s\t%s\t%s\t%s", 
							contigName,refStart_curr,refEnd_curr, orientation, numberOfReads);
					temp_range_contig = new Range(refStart_curr, refEnd_curr, str);
					break;
				}
				case seqread :
				{
						String str = ((String)event.getSource()).trim();
						for (int i = 0; i < str.length(); i++)
						{
							char ch = str.charAt(i);
							if (ch == 'C' || ch == 'c' || ch == 'g' || ch == 'G')
								cgContent++;
						}
						seqReadLength = seqReadLength + str.length();
						break;
					
				}
				case docEnded : 
				{
					if (temp_range_contig != null)
					{
						String str = String.format("%s\t", temp_range_contig.getMetadata(),seqReadLength,cgContent);
						temp_range_contig.setMetadata(str);
						eachContigInfo.add(temp_range_contig);
						seqReadLength = 0;
						cgContent = 0;
						
						
						totalContigLength = totalContigLength + seqReadLength;
						totalCGContent = totalCGContent + cgContent;
						if (referenceGiven)
						{
							totalMappedLength = totalMappedLength + 
							(temp_range_contig.getUpperLimit()- temp_range_contig.getLowerLimit() + 1);
							
							if (considerGaps && temp_range_contig.getUpperLimit() < referenceSize)
							{
								int gap_start = temp_range_contig.getUpperLimit() + 1;
								int gap_end = referenceSize;
								int length = gap_end - gap_start + 1;
								str = String.format("%s\t%s%s\t%s\t%s",eachGapInfo.size(),gap_start,gap_end,length,
										temp_range_contig.getMetadata().split("\t")[0]);
								Range gap = new Range(gap_start, gap_end,str);
								eachGapInfo.add(gap);
								totalGapSize = totalGapSize + length;
							}
							
							Collections.sort(eachContigInfo,new RangeComparator());
							if (considerGaps)
							{
								Range contig = eachContigInfo.get(0);
								if (contig.getLowerLimit() != 1)
								{
									int gap_start = 1;
									int gap_end = contig.getLowerLimit() - 1;
									int length = gap_end - gap_start + 1;
									str = String.format("%s\t%s%s\t%s\t%s",eachGapInfo.size(),gap_start,gap_end,length,
											contig.getMetadata().split("\t")[0]);
									Range gap = new Range(gap_start, gap_end,str);
									eachGapInfo.add(gap);
									totalGapSize = totalGapSize + length;
								}
								Collections.sort(eachGapInfo,new RangeComparator());
							}
						}
						temp_range_contig = null;
					}
					break;
				}
			}
		}
		catch(Exception e)
		{
			e.printStackTrace();
			System.exit(1);
		}
	}
	
	public ContigsStatsGenerator setOut_scaffoldStatsFile(String out_scaffoldStatsFile) {
		this.out_scaffoldStatsFile = out_scaffoldStatsFile;
		return this;
	}

	public ContigsStatsGenerator setOut_gapStatsFile(String out_gapStatsFile) {
		this.out_gapStatsFile = out_gapStatsFile;
		return this;
	}
	
	public ContigsStatsGenerator setInputScaffoldFile(String inputScaffoldFile) {
		this.inputScaffoldFile = inputScaffoldFile;
		return this;
	}
	
	public ContigsStatsGenerator setOut_summaryFile(String out_summaryFile) {
		this.out_summaryFile = out_summaryFile;
		return this;
	}
	
	public ContigsStatsGenerator setConsiderGaps(boolean doConsider)
	{
		this.considerGaps = doConsider;
		return this;
	}
	
	public ContigsStatsGenerator setSeqReadLength(int seqReadLength) {
		this.referenceSize = seqReadLength;
		return this;
	}
	
	public ContigsStatsGenerator setStrainName(String strainName) {
		this.strainName = strainName;
		return this;
	}
	
	public ContigsStatsGenerator setAppendSummary(boolean append) {
		this.appendSummary = append;
		return this;
	}

	public ArrayList<Range> getEachContigInfo() {
		return eachContigInfo;
	}

	public ArrayList<Range> getEachGapInfo() {
		return eachGapInfo;
	}
	
} // ScaffoldStatsGenerator
